package ksvc

import (
    "encoding/json"
    "errors"
    "fcs-api/src/app/model"
    k8s "fcs-api/src/client/kubernetes"
    "fmt"
    "strconv"
    "strings"
    "time"

    apierrors "k8s.io/apimachinery/pkg/api/errors"

    "git.inspur.com/x/common/loggerfactory"
    corev1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/api/resource"
    metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    kapiV1 "knative.dev/serving/pkg/apis/serving/v1"
)

var logger = loggerfactory.GetLogger()

func CreateKsvc(fcClient *k8s.FcClient, functionInfo *model.FuncInfo, imageUrl string, vpcConfig *model.VpcConfig) (*kapiV1.Service, error) {

    namespace := GetFunctionNamespace(functionInfo.A5tId, functionInfo.GroupName)
    svcSpec, err := getKSvcStruct(functionInfo, imageUrl, namespace, vpcConfig)
    if err != nil {
        _ = logger.Error("CreateKsvc: create knative service struct error, %v", err)
        return nil, err
    }

    ksvcInfo, err := fcClient.CreateKnativeService(namespace, svcSpec)
    if err != nil {
        _ = logger.Error("CreateKsvc: create knative service error, %v", err)
        return nil, err
    }
    return ksvcInfo, nil
}

func UpdateKsvcTraffic(fcClient *k8s.FcClient, functionInfo *model.FuncInfo) (*kapiV1.Service, error) {
    namespace := GetFunctionNamespace(functionInfo.A5tId, functionInfo.GroupName)
    return updateKsvcTraffic(fcClient, functionInfo.FunctionName, namespace, functionInfo.VersionName)
}

func DeleteKsvc(fcClient *k8s.FcClient, functionInfo *model.FuncInfo) error {

    namespace := GetFunctionNamespace(functionInfo.A5tId, functionInfo.GroupName)
    err := fcClient.DeleteKnativeService(namespace, functionInfo.FunctionName)
    if err != nil {
        if !strings.Contains(err.Error(), "not found") {
            _ = logger.Error("DeleteKsvc: Delete Ksvc error, %v", err)
            return err
        }
    }
    return nil
}

func UpdateKsvc(fcClient *k8s.FcClient, updateModel *model.UpdateModel, vpcConfig *model.VpcConfig) (*kapiV1.Service, error) {

    functionInfo := updateModel.FuncInfo
    namespace := GetFunctionNamespace(functionInfo.A5tId, functionInfo.GroupName)
    ksvcInfo, err := fcClient.GetKnativeService(namespace, functionInfo.FunctionName)
    if err != nil {
        _ = logger.Error("UpdateKsvc: get Ksvc error, %v", err)
        return nil, err
    }
    enablePublic := "false"
    if functionInfo.EnablePublic == 1 {
        enablePublic = "true"
    }

    if ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Labels[model.FunctionRuntimeLabelKey] == model.CustomContainer {
        // containerImage
        imageUrl := model.Base.ContainerImageRegistry + "/" + functionInfo.ContainerImage
        ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Image = imageUrl
        // containerPort
        userPort, err := strconv.Atoi(functionInfo.ContainerPort)
        if err != nil {
            return nil, err
        }
        ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{
            {
                ContainerPort: int32(userPort),
            },
        }
        // containerCommand
        command, err := stringToStringArray(functionInfo.ContainerCommand)
        if err != nil {
            return nil, err
        }
        ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Command = command
        // containerArgs
        args, err := stringToStringArray(functionInfo.ContainerArgs)
        if err != nil {
            return nil, err
        }
        ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Args = args
    } else {
        if functionInfo.InitHandler != "" && functionInfo.InitTimeout != 0 {
            initTimeout := strconv.Itoa(functionInfo.InitTimeout)
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.InitializeHandlerLabelKey] = functionInfo.InitHandler
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.InitializeTimeoutLabelKey] = initTimeout
        } else {
            delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.InitializeHandlerLabelKey)
            delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.InitializeTimeoutLabelKey)
        }
    }

    memory, err := resource.ParseQuantity(strconv.Itoa(functionInfo.MemorySize) + model.KsvcMemUnit)
    if err != nil {
        _ = logger.Error("UpdateKsvc: update knative service memory, %v", err)
        return nil, err
    }
    timeout := int64(functionInfo.Timeout)
    concurrency := int64(functionInfo.Concurrency)
    ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Labels[model.VersionLabelKey] = functionInfo.VersionName
    ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.FunctionHandlerLabelKey] = functionInfo.Handler
    ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.MinScaleAnnotationKey] = strconv.Itoa(functionInfo.ReserveNumber)
    ksvcInfo.Spec.ConfigurationSpec.Template.Spec.TimeoutSeconds = &timeout
    ksvcInfo.Spec.ConfigurationSpec.Template.Spec.ContainerConcurrency = &concurrency

    //add iamUrl Env for sdk
    iamUrl := strings.Split(model.Base.IamApiServerHost, "/a")[0]
    environmentIam := make([]model.Environment, len(functionInfo.Envs))
    copy(environmentIam, functionInfo.Envs)
    environmentIam = append(environmentIam, model.Environment{Key: "iamUrl", Value: iamUrl})

    ksvcInfo.Spec.ConfigurationSpec.Template.Spec.PodSpec.Containers[0].Env = transEnv(environmentIam)
    ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
        corev1.ResourceMemory: memory,
    }
    ksvcInfo.Spec.ConfigurationSpec.Template.Labels[model.PublicLabelKey] = enablePublic

    // 层相关
    useLayer := "false"
    if len(functionInfo.LayerPath) > 0 {
        useLayer = "true"
    } else {
        // 防止出现json解析为null的情况
        functionInfo.LayerPath = make([]string, 0)
    }
    layerJson, _ := json.Marshal(functionInfo.LayerPath)
    ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.LayerAnnotationKey] = string(layerJson)
    ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Labels[model.LayerUsedLabelKey] = useLayer

    if functionInfo.EnableVpc == 1 {
        if vpcConfig != nil {
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.EthNameAnnotationKey] = vpcConfig.Eth
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.GWAnnotationKey] = vpcConfig.Gateway
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.CIDRAnnotationKey] = vpcConfig.Cidr
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.RemoteAnnotationKey] = vpcConfig.Remote
            ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.MACAnnotationKey] = vpcConfig.Mac
        }
    } else {
        delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.EthNameAnnotationKey)
        delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.GWAnnotationKey)
        delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.CIDRAnnotationKey)
        delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.RemoteAnnotationKey)
        delete(ksvcInfo.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations, model.MACAnnotationKey)
    }
    ksvcInfo.Spec.ConfigurationSpec.Template.Spec.PodSpec.Affinity = getAffinity(functionInfo.EnablePublic)
    //更新gpu相关
    if functionInfo.GpuNum <= 0 {
        l := ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Resources.Limits[model.GpuLimitName]
        if (&l).Value() != 0 {
            delete(ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Resources.Limits, model.GpuLimitName)
        }
        if ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector[model.GpuTypeName] != "" {
            delete(ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector, model.GpuTypeName)
        }
    } else {
        gpuNum, err := resource.ParseQuantity(strconv.Itoa(functionInfo.GpuNum))
        if err != nil {
            _ = logger.Error("UpdateKsvc: update knative service gpuNum, %v", err)
            return nil, err
        }
        ksvcInfo.Spec.ConfigurationSpec.Template.Spec.Containers[0].Resources.Limits[model.GpuLimitName] = gpuNum
        if functionInfo.GpuType != model.GpuCommonType {
            if ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector != nil {
                ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector[model.GpuTypeName] = functionInfo.GpuType
            } else {
                ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector = map[string]string{model.GpuTypeName: functionInfo.GpuType}
            }
        } else {
            delete(ksvcInfo.Spec.ConfigurationSpec.Template.Spec.NodeSelector, model.GpuTypeName)
        }
    }

    ksvcInfo, err = fcClient.UpdateKnativeService(namespace, ksvcInfo)
    if err != nil {
        _ = logger.Error("UpdateKsvc: update knative service error, %v", err)
        return nil, err
    }
    return ksvcInfo, err
}

// 删除函数版本的时候，首先更改ksvc的traffic，
// 然后再删除对应的revision
func DeleteVersion(fcClient *k8s.FcClient, functionInfo *model.FuncInfo) error {
    namespace := GetFunctionNamespace(functionInfo.A5tId, functionInfo.GroupName)
    ksvcInfo, err := fcClient.GetKnativeService(namespace, functionInfo.FunctionName)
    if err != nil {
        _ = logger.Error("DeleteVersion: get Ksvc error, %v", err)
        return err
    }
    tag := "v" + functionInfo.VersionName
    var revisionName string
    for index, traffic := range ksvcInfo.Spec.Traffic {
        if traffic.Tag == tag {
            // 防止下标越界
            if index != len(ksvcInfo.Spec.Traffic)-1 {
                ksvcInfo.Spec.Traffic = append(ksvcInfo.Spec.Traffic[:index], ksvcInfo.Spec.Traffic[index+1:]...)
            } else {
                ksvcInfo.Spec.Traffic = ksvcInfo.Spec.Traffic[:index]
            }
            revisionName = traffic.RevisionName
            break
        }
    }
    // 如果删除时没有找到对应的revision，不认为是失败
    // knative有垃圾回收机制，会删除不用的revision
    if revisionName == "" {
        _ = logger.Warn("DeleteVersion: didn't find match revision ksvc is %s, tag is %s, namespace is %s", functionInfo.FunctionName, tag, namespace)
        return nil
    }
    ksvcInfo, err = fcClient.UpdateKnativeService(namespace, ksvcInfo)
    if err != nil {
        _ = logger.Error("DeleteVersion: update Ksvc error, %v", err)
        return err
    }
    err = fcClient.DeleteKnativeRevision(namespace, revisionName)
    if err != nil {
        _ = logger.Error("DeleteVersion: delete revision error, %v", err)
        return err
    }
    return nil
}

// 更新ksvc路由
func updateKsvcTraffic(fcClient *k8s.FcClient, ksvcName, namespace, version string) (*kapiV1.Service, error) {

    readyChan := make(chan bool)
    li := model.ListenInfo{
        Name:      ksvcName,
        Namespace: namespace,
        ReadyChan: readyChan,
        StartTime: time.Now(),
    }
    model.Base.Queue <- li
    ready := <-readyChan
    if !ready {
        _ = logger.Error("updateKsvcTraffic: ksvc not ready ksvcName: %s", ksvcName)
        return nil, errors.New("ksvc not ready")
    }

    return updateRevisionTag(fcClient, ksvcName, namespace, version)
}

// 找到对应版本的revision，并修改流量信息
func updateRevisionTag(fcClient *k8s.FcClient, ksvcName, namespace, version string) (*kapiV1.Service, error) {
    deleteChan := make(chan int, 1) // 设置1个缓冲，防止后面阻塞
    defer close(deleteChan)
    revision, err := getVersionRevision(fcClient, ksvcName, namespace, version, deleteChan)
    if err != nil {
        _ = logger.Error("updateKsvcTraffic: getVersionRevision error, %v", err)
        return nil, err
    }
    ksvc, err := updateKsvcTrafficTag(fcClient, ksvcName, revision.Name, version, namespace)
    if err != nil {
        _ = logger.Error("updateKsvcTraffic: updateKsvcTrafficTag error, %v", err)
        return nil, err
    }
    deleteChan <- 1
    return ksvc, nil
}

func isKsvcReadyLoop(fcClient *k8s.FcClient, name, namespace string, ready chan bool) {

    // 先休眠1s，防止操作过快引起的问题
    time.Sleep(time.Second)
    // 循环检测ksvc是否ready
    for i := 0; i < model.Base.KsvcWaitingTime; i++ {
        ksvcInfo, err := fcClient.GetKnativeService(namespace, name)
        if err != nil {
            logger.Info("isKsvcReadyLoop: get ksvc err, %s", err.Error())
            if apierrors.IsNotFound(err) {
                ready <- false
                return
            }
        } else if ksvcInfo.IsReady() {
            ready <- true
            return
        }
        time.Sleep(time.Second)
    }
    ready <- false
}

func updateKsvcTrafficTag(fcClient *k8s.FcClient, ksvcName, revisionName, version, namespace string) (*kapiV1.Service, error) {

    ksvcInfo, err := fcClient.GetKnativeService(namespace, ksvcName)
    if err != nil {
        _ = logger.Error("updateKsvcTrafficTag: get ksvc info error, %v", err)
        return nil, err
    }

    latestRevision := false
    percent := int64(100)
    tag := strings.ToLower(version) // tag必须是小写
    if version != model.FunctionLatestVersion {
        percent = int64(0)
        tag = "v" + version // knative的tag不能是数字
    }
    // 更新流量路由信息
    trafficTarget := kapiV1.TrafficTarget{
        Tag:            tag,
        RevisionName:   revisionName,
        LatestRevision: &latestRevision,
        Percent:        &percent,
    }
    // 将初始的流量配置覆盖
    if version == model.FunctionLatestVersion {
        ksvcInfo.Spec.Traffic[0] = trafficTarget
    } else {
        // 如果是其他版本，在后面进行追加
        ksvcInfo.Spec.Traffic = append(ksvcInfo.Spec.Traffic, trafficTarget)
    }
    ksvcInfo, err = fcClient.UpdateKnativeService(namespace, ksvcInfo)
    if err != nil {
        _ = logger.Error("updateKsvcTrafficTag: update ksvc traffic error, %v", err)
        return nil, err
    }
    return ksvcInfo, nil
}

func getVersionRevision(fcClient *k8s.FcClient, ksvcName, namespace, version string, deleteChan chan int) (*kapiV1.Revision, error) {
    // 这里使用label selector进行revision的筛选，没有使用 ksvc中Latest Ready Revision Name字段
    // 在并发的情况下，如果有请求同时进行修改，Latest Ready Revision Name字段是最后一个建出的版本，可能和version不对应
    // 使用label筛选的话，可以直接筛选出对应的version，避免Latest Ready Revision Name不准确的情况
    labelSelector := fmt.Sprintf("%s=%s,%s=%s", model.VersionLabelKey, version, model.FunctionNameLabelKey, ksvcName)

    revisionList, err := fcClient.GetKnativeRevisionList(namespace, labelSelector)
    if err != nil {
        _ = logger.Error("getVersionRevision: query revisionList error, %v", err)
        return nil, err
    }
    if len(revisionList.Items) == 0 {
        _ = logger.Error("getVersionRevision: query revisionList count is 0")
        return nil, errors.New("revisionList count is 0")
    }

    // 默认返回revision数组中第一条数据
    revisionIndex := 0
    // 处理有多个latest版本的情况
    if version == model.FunctionLatestVersion && len(revisionList.Items) > 1 {
        maxGeneration := 0
        // 选出最新的revision版本
        for index, item := range revisionList.Items {
            generation, err := strconv.Atoi(item.Labels[model.RevisionGenerationLabel])
            if err != nil {
                _ = logger.Error("getVersionRevision: generation transform error, %v", err)
                return nil, err
            }
            if generation > maxGeneration {
                // 修改返回revision数组中最新的数据
                revisionIndex = index
                maxGeneration = generation
            }
        }

        // 如果有多个revision版本，清除多余的
        go func() {
            _, ok := <-deleteChan
            if ok {
                for index, revision := range revisionList.Items {
                    if index != revisionIndex {
                        err = fcClient.DeleteKnativeRevision(namespace, revision.Name)
                        if err != nil {
                            _ = logger.Error("getVersionRevision: delete revision error, %v", err)
                        }
                    }
                }
            }
        }()
    }
    return &revisionList.Items[revisionIndex], nil
}

func getKSvcStruct(functionInfo *model.FuncInfo, imageUrl, namespace string, vpcConfig *model.VpcConfig) (
    *kapiV1.Service, error) {
    memory, err := resource.ParseQuantity(strconv.Itoa(functionInfo.MemorySize) + model.KsvcMemUnit)
    if err != nil {
        _ = logger.Error("getKSvcStruct: memory error, %v", err)
        return nil, err
    }
    timeout := int64(functionInfo.Timeout)
    concurrency := int64(functionInfo.Concurrency)
    enablePublic := "false"
    if functionInfo.EnablePublic == 1 {
        enablePublic = "true"
    }
    enableServiceLinks := false
    //add iamUrl Env for sdk
    iamUrl := strings.Split(model.Base.IamApiServerHost, "/a")[0]
    environmentIam := make([]model.Environment, len(functionInfo.Envs))
    copy(environmentIam, functionInfo.Envs)
    environmentIam = append(environmentIam, model.Environment{Key: "iamUrl", Value: iamUrl})

    imageTag := strings.Split(imageUrl, ":")[len(strings.Split(imageUrl, ":"))-1]
    ksvc := &kapiV1.Service{
        TypeMeta: metaV1.TypeMeta{
            Kind:       "Service",
            APIVersion: "serving.knative.dev/v1",
        },
        ObjectMeta: metaV1.ObjectMeta{
            Name: functionInfo.FunctionName,
        },
        Spec: kapiV1.ServiceSpec{
            ConfigurationSpec: kapiV1.ConfigurationSpec{
                Template: kapiV1.RevisionTemplateSpec{
                    ObjectMeta: metaV1.ObjectMeta{
                        Labels: map[string]string{
                            model.VersionLabelKey:            model.FunctionLatestVersion,
                            model.FunctionIdLabelKey:         functionInfo.Id,
                            model.GroupIdLabelKey:            functionInfo.GroupId,
                            model.FunctionRuntimeLabelKey:    functionInfo.Runtime,
                            model.PublicLabelKey:             enablePublic,
                            model.FunctionRuntimeTagLabelKey: imageTag,
                            model.FunctionNamespaceKey:       namespace,
                            model.ICPLabelKey:                model.Service,
                        },
                        Annotations: map[string]string{
                            model.KataContainerAnnotationKey: "true",
                            model.MinScaleAnnotationKey:      strconv.Itoa(functionInfo.ReserveNumber),
                            model.MaxScaleAnnotationKey:      "10", // 设置最大实例数量
                            model.FunctionHandlerLabelKey:    functionInfo.Handler,
                        },
                    },
                    Spec: kapiV1.RevisionSpec{
                        TimeoutSeconds:       &timeout,
                        ContainerConcurrency: &concurrency,
                        PodSpec: corev1.PodSpec{
                            EnableServiceLinks: &enableServiceLinks, // 不在pod中设置其他服务的环境变量
                            Containers: []corev1.Container{
                                {
                                    Image: imageUrl,
                                    Env:   transEnv(environmentIam),
                                    Resources: corev1.ResourceRequirements{
                                        Limits: map[corev1.ResourceName]resource.Quantity{
                                            corev1.ResourceMemory: memory,
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
            },
        },
    }
    // custom-container
    if functionInfo.Runtime == model.CustomContainer {
        ksvc.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullAlways
        ksvc.Spec.Template.Spec.ImagePullSecrets = []corev1.LocalObjectReference{
            {
                Name: model.DefaultRegistrySecretName,
            },
        }
        if functionInfo.ContainerPort != "" {
            userPort, err := strconv.Atoi(functionInfo.ContainerPort)
            if err != nil {
                return nil, err
            }
            ksvc.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{
                {
                    ContainerPort: int32(userPort),
                },
            }
        }
        if functionInfo.ContainerCommand != "" {
            command, err := stringToStringArray(functionInfo.ContainerCommand)
            if err != nil {
                return nil, err
            }
            ksvc.Spec.Template.Spec.Containers[0].Command = command
        }
        if functionInfo.ContainerArgs != "" {
            args, err := stringToStringArray(functionInfo.ContainerArgs)
            if err != nil {
                return nil, err
            }
            ksvc.Spec.Template.Spec.Containers[0].Args = args
        }
    } else {
        if functionInfo.InitHandler != "" && functionInfo.InitTimeout != 0 {
            initTimeout := strconv.Itoa(functionInfo.InitTimeout)
            ksvc.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.InitializeHandlerLabelKey] = functionInfo.InitHandler
            ksvc.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations[model.InitializeTimeoutLabelKey] = initTimeout
        }
    }
    //设置gpu相关
    gpuNum, err := resource.ParseQuantity(strconv.Itoa(functionInfo.GpuNum))
    if err != nil {
        _ = logger.Error("getKSvcStruct: update knative service gpuNum, %v", err)
        return nil, err
    }
    if functionInfo.GpuNum > 0 {
        ksvc.Spec.Template.Spec.PodSpec.Containers[0].Resources.Limits[model.GpuLimitName] = gpuNum
        if functionInfo.GpuType != model.GpuCommonType {
            ksvc.Spec.Template.Spec.PodSpec.NodeSelector = map[string]string{model.GpuTypeName: functionInfo.GpuType}
        }
    }
    // 设置vpc相关标签
    //if functionInfo.EnableVpc == 1 {
    //	ksvc.Spec.Template.ObjectMeta.Annotations[model.EthNameAnnotationKey] = vpcConfig.Eth
    //	ksvc.Spec.Template.ObjectMeta.Annotations[model.GWAnnotationKey] = vpcConfig.Gateway
    //	ksvc.Spec.Template.ObjectMeta.Annotations[model.CIDRAnnotationKey] = vpcConfig.Cidr
    //	ksvc.Spec.Template.ObjectMeta.Annotations[model.RemoteAnnotationKey] = vpcConfig.Remote
    //	ksvc.Spec.Template.ObjectMeta.Annotations[model.MACAnnotationKey] = vpcConfig.Mac
    //}

    //设置进程快照
    if functionInfo.Checkpoint == "true" {
        envs := []corev1.EnvVar{
            corev1.EnvVar{Name: "OSS_ENDPOINT", Value: model.Base.OssEndpoint},
            corev1.EnvVar{Name: "BUCKET", Value: model.Base.OssBucket},
            corev1.EnvVar{Name: "AK", Value: model.Base.OssAK},
            corev1.EnvVar{Name: "SK", Value: model.Base.OssSK},
            corev1.EnvVar{Name: "AccountId", Value: functionInfo.A5tId},
            corev1.EnvVar{Name: "GroupName", Value: functionInfo.GroupName},
            corev1.EnvVar{Name: "FunctionName", Value: functionInfo.FunctionName},
            corev1.EnvVar{Name: "CACHE", Value: "enabled"},
        }
        ksvc.Spec.Template.Spec.PodSpec.Containers[0].Env = append(ksvc.Spec.Template.Spec.PodSpec.Containers[0].Env, envs...)
    }

    // 添加是否使用层的标签
    useLayer := "false"
    if len(functionInfo.LayerPath) > 0 {
        useLayer = "true"
    } else {
        // 防止出现json解析为null的情况
        functionInfo.LayerPath = make([]string, 0)
    }
    // 将层路径以json数组的方式添加到ksvc信息中
    layerJson, err := json.Marshal(functionInfo.LayerPath)
    if err != nil {
        _ = logger.Error("getKSvcStruct: marshal layer path error, %v", err)
        return nil, err
    }
    ksvc.Spec.Template.ObjectMeta.Annotations[model.LayerAnnotationKey] = string(layerJson)
    ksvc.Spec.Template.ObjectMeta.Labels[model.LayerUsedLabelKey] = useLayer

    // 公网访问配置
    ksvc.Spec.Template.Spec.Affinity = getAffinity(functionInfo.EnablePublic)

    return ksvc, nil
}

// 输入示例：["python", "server.py"]
// 即，为 JSON 序列化后的数组
func stringToStringArray(str string) ([]string, error) {
    if str == "" {
        return []string{}, nil
    }

    var result []string
    err := json.Unmarshal([]byte(str), &result)
    if err != nil {
        logger.Error("stringToStringArray error %s, input date %s", err.Error(), str)
        return nil, err
    }
    return result, nil
}

func getAffinity(enablePublic int) *corev1.Affinity {
    operator := corev1.NodeSelectorOpNotIn
    if enablePublic == 1 {
        operator = corev1.NodeSelectorOpIn
    }
    return &corev1.Affinity{
        NodeAffinity: &corev1.NodeAffinity{
            RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{[]corev1.NodeSelectorTerm{
                {
                    MatchExpressions: []corev1.NodeSelectorRequirement{
                        {
                            Key:      "accessNetwork",
                            Operator: operator,
                            Values:   []string{"true"},
                        },
                    },
                },
            }},
        },
    }
}

func transEnv(environmentList []model.Environment) []corev1.EnvVar {
    if environmentList == nil {
        return nil
    }
    envSlice := make([]corev1.EnvVar, len(environmentList))
    for index, environment := range environmentList {
        env := corev1.EnvVar{
            Name:  environment.Key,
            Value: environment.Value,
        }
        envSlice[index] = env
    }
    return envSlice
}

func GetFunctionNamespace(a5tId, groupName string) string {
    return model.FunctionNamespacePrefix + "-" + a5tId + "-" + groupName
}

func GetAliasNamespace(a5tId, groupName string) string {
    return model.FunctionNamespacePrefix + "-" + a5tId + "-" + groupName
}

func GetGroupNs(group *model.Group) string {
    return model.FunctionNamespacePrefix + "-" + group.A5tId + "-" + group.Name
}

func GetTriggerNs(trigger *model.Trigger) string {
    return model.FunctionNamespacePrefix + "-" + trigger.A5tId + "-" + trigger.GroupName
}
